package com.eolcum.evo.thread;

import java.io.IOException;
import java.util.Calendar;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.util.Log;

import com.eolcum.evo.persistence.Apartment;
import com.eolcum.evo.persistence.Sensor;
import com.google.gson.Gson;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;

public class SerialCommunicationManager implements Runnable {

	private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
	private SerialInputOutputManager mSerialIoManager;
	private UsbSerialDriver mSerialDevice;
	private UsbManager usbManager;
	private Context context;
	private String messageLine = "";
	private String lastMessageLine;
	private long lastMessageTime = 0L;

	private Apartment apartment;

	@Override
	public void run() {

		try {
			while (mSerialDevice == null) {
				mSerialDevice = UsbSerialProber.acquire(usbManager);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			mSerialDevice.open();
			mSerialDevice.setBaudRate(9600);
			Log.i("SerialCommunicationManager", "Serial device opened with baudrate:9600\n");
		} catch (IOException e) {
			Log.i("SerialCommunicationManager", "Error opening device: " + e.getMessage());
			try {
				mSerialDevice.close();
			} catch (IOException e2) {
			}
			mSerialDevice = null;
			return;
		}
		Log.i("SerialCommunicationManager", "Serial device: " + mSerialDevice);

		onDeviceStateChange();

	}

	private void updateReceivedData(byte[] data) {
		for (int k = 0; k < data.length; k++) {
			if (data[k] > ' ' && data[k] < '~') {
				String mChar = new String(data, k, 1);
				if ("\\".equals(mChar)) {
					if ((lastMessageLine == null || !lastMessageLine.equals(messageLine)) &&
						(lastMessageTime == 0L || lastMessageTime / 1000 != Calendar.getInstance().getTimeInMillis() / 1000)) {
						Log.i("SerialCommunicationManager", messageLine);
						lastMessageTime = Calendar.getInstance().getTimeInMillis();
						double temperature = -1;
						boolean occupancy = false;
						double humidity = -1;
						String sensorId = "";
						try {
							sensorId = messageLine.split(":")[0];
							temperature = Double.parseDouble(messageLine.split(":")[2]);
							occupancy = "1".equals(messageLine.split(":")[1]);
							humidity = Double.parseDouble(messageLine.split(":")[3]);

							for (int i = 0; i < apartment.getRooms().size(); i++) {
								boolean breaked = false;
								for (int j = 0; j < apartment.getRooms().get(i).getSensors().size(); j++) {
									if (apartment.getRooms().get(i).getSensors().get(j).getSerialNo().equals(sensorId)) {
										Sensor s = apartment.getRooms().get(i).getSensors().get(j);
										if (s.isHasTemp()) {
											apartment.getRooms().get(i).getSensors().get(j).setActiveTemp(temperature);
											apartment.getRooms().get(i).updateActualTemperature();
										}
										if (s.isHasOccup()) {
											apartment.getRooms().get(i).getSensors().get(j).setActiveOccupied(occupancy);
											apartment.getRooms().get(i).updateActualOccupancy();
										}
										if (s.isHasHumid()) {
											apartment.getRooms().get(i).getSensors().get(j).setActiveHumidity(humidity);
											apartment.getRooms().get(i).updateActualHumidity();
										}
										
										Calendar cal = Calendar.getInstance();
										apartment.getRooms().get(i).getSensors().get(j).setLastActiveTimeInMillis(cal.getTimeInMillis());
										Log.i("SerialCommunicationManager",
												messageLine + "  " + cal.get(Calendar.DAY_OF_MONTH) + "/"
														+ (cal.get(Calendar.MONTH) + 1) + "/" + cal.get(Calendar.YEAR)
														+ " " + cal.get(Calendar.HOUR_OF_DAY) + ":"
														+ cal.get(Calendar.MINUTE) + " sensorId=" + sensorId
														+ ", occupancy=" + occupancy + ", temperature=" + temperature);

										Gson gson = new Gson();
//										Intent intent = new Intent();
//										intent.setAction("com.eolcum.evo.EVO_LOGGER");
//										intent.putExtra("function_name", "getSensor");
//										intent.putExtra("sensor", gson.toJson(apartment.getRooms().get(i).getSensors().get(j)));
//										context.sendBroadcast(intent);

										Intent intent = new Intent();
										intent.setAction("com.eolcum.evo.service.EvoLoggerService");
										intent.putExtra("function_name", "getSensor");
										intent.putExtra("sensor", gson.toJson(apartment.getRooms().get(i).getSensors().get(j)));
										context.startService(intent);
										
										breaked = true;
										break;
									}
								}
								if (breaked)
									break;
							}
						} catch (Exception e) {}
						lastMessageLine = messageLine;
					}
					messageLine = "";
				} else {
					messageLine = messageLine + mChar;
				}
			}
		}
	}

	private void stopIoManager() {
		if (mSerialIoManager != null) {
			mSerialIoManager.stop();
			mSerialIoManager = null;
		}
	}

	private void startIoManager() {
		if (mSerialDevice != null) {
			mSerialIoManager = new SerialInputOutputManager(mSerialDevice, mListener);
			mExecutor.submit(mSerialIoManager);
		}
	}

	private void onDeviceStateChange() {
		stopIoManager();
		startIoManager();
	}

	public Apartment getApartment() {
		return apartment;
	}

	public void setApartment(Apartment apartment) {
		this.apartment = apartment;
	}

	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}
	
	public void setmSerialDevice(UsbSerialDriver mSerialDevice) {
		this.mSerialDevice = mSerialDevice;
	}
	
	public UsbManager getUsbManager() {
		return usbManager;
	}

	public void setUsbManager(UsbManager usbManager) {
		this.usbManager = usbManager;
	}

	private final SerialInputOutputManager.Listener mListener = new SerialInputOutputManager.Listener() {

		@Override
		public void onRunError(Exception e) {

		}

		@Override
		public void onNewData(final byte[] data) {
			SerialCommunicationManager.this.updateReceivedData(data);
		}
	};

}
